

package com.maoshi.shop.coupon.comment.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.maoshi.shop.bean.app.dto.CouponDto;
import com.maoshi.shop.bean.app.dto.CouponOrderDto;
import com.maoshi.shop.bean.app.dto.ProductDto;
import com.maoshi.shop.bean.enums.CouponProdType;
import com.maoshi.shop.bean.enums.OfflineHandleEventStatus;
import com.maoshi.shop.bean.enums.OfflineHandleEventType;
import com.maoshi.shop.bean.event.ScoreOrderEvent;
import com.maoshi.shop.bean.model.OfflineHandleEvent;
import com.maoshi.shop.bean.param.OfflineHandleEventAuditParam;
import com.maoshi.shop.common.exception.MaoshiShopBindException;
import com.maoshi.shop.common.util.PageParam;
import com.maoshi.shop.coupon.comment.Enums.CouponStatusEnum;
import com.maoshi.shop.coupon.comment.dao.CouponMapper;
import com.maoshi.shop.coupon.comment.dao.CouponProdMapper;
import com.maoshi.shop.coupon.comment.dao.CouponUserMapper;
import com.maoshi.shop.coupon.comment.model.Coupon;
import com.maoshi.shop.coupon.comment.model.CouponProd;
import com.maoshi.shop.coupon.comment.model.CouponUser;
import com.maoshi.shop.coupon.comment.service.CouponService;
import com.maoshi.shop.coupon.comment.service.CouponUserService;
import com.maoshi.shop.dao.ProductMapper;
import com.maoshi.shop.service.OfflineHandleEventService;
import com.maoshi.shop.service.UserService;
import lombok.AllArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lgh on 2018/12/27.
 */
@Service
@AllArgsConstructor
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {

    private final CouponMapper couponMapper;
    private final UserService userService;
    private final CouponProdMapper couponProdMapper;
    private final CouponUserMapper couponUserMapper;
    private final CouponUserService couponUserService;
    private final ProductMapper productMapper;
    private final OfflineHandleEventService offlineHandleEventService;
    private ApplicationContext applicationContext;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#coupon.shopId")
    public void updateCouponAndCouponProds(@Valid Coupon coupon) {
        Integer suitableProdType = coupon.getSuitableProdType();
        // 判断该商品的类型
        if ((Objects.equals(CouponProdType.PROD_IN.value(), coupon.getSuitableProdType())
                || Objects.equals(CouponProdType.PROD_NO_IN.value(), coupon.getSuitableProdType()))
                && CollectionUtils.isEmpty(coupon.getCouponProds())) {
            throw new MaoshiShopBindException("商品不能为空");
        }
        // 结束时间小于等于当前时间
        if (coupon.getValidTimeType() == 1) {
            Date nowTime = new Date();
            if (coupon.getEndTime().getTime() < nowTime.getTime()) {
                coupon.setOverdueStatus(0);
                coupon.setPutonStatus(0);
            } else {
                coupon.setOverdueStatus(1);
            }
        }
        couponMapper.updateById(coupon);
        // 删除该优惠券下所有商品
        couponProdMapper.deleteCouponProdsByCouponId(coupon.getCouponId());
        if (suitableProdType != 0) {
            List<Long> prodIds = coupon.getCouponProds().stream().map(CouponProd::getProdId).collect(Collectors.toList());
            // 插入所有商品
            couponProdMapper.insertCouponProdsBatch(coupon.getCouponId(), prodIds);
        }
    }

    @Override
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#shopId")
    public void removeCouponAndCouponProdsCache(Long shopId) {
    }

    @Override
    public List<CouponDto> getCouponList() {
        List<CouponDto> couponDtoList =  couponMapper.getCouponList();
        List<ProductDto> productDtos = null;
        for (CouponDto couponDto : couponDtoList){
            int index = 0;
            if (Objects.equals(couponDto.getSuitableProdType(),1)){
                productDtos = new ArrayList<ProductDto>();
                for (ProductDto productDto: couponDto.getProds()){
                   productDtos.add(productDto);
                    index++;
                    if (index == 3 ) {
                        break;
                    }
                }
            }else if (Objects.equals(couponDto.getSuitableProdType(),0)){
                productDtos = productMapper.getcouponProdList(null, couponDto.getShopId());
            }else {
                Long[] prods = new Long[couponDto.getProds().size()];
                for (ProductDto productDto: couponDto.getProds()){
                    prods[index] = productDto.getProdId();
                    index++;
                }
               productDtos = productMapper.getcouponProdList(prods, couponDto.getShopId());
            }
            couponDto.setProds(productDtos);
        }
        return couponDtoList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#shopId")
    public void batchBindCouponByIds(List<Long> couponIds, String userId,Long shopId) {
        List<CouponUser> couponUsers = new ArrayList<>();
        Collection<Coupon> coupons = listByIds(couponIds);
        for (Coupon coupon : coupons) {
            CouponUser couponUser = getCouponUser(coupon, userId);
            couponUsers.add(couponUser);
        }
        couponUserService.saveBatch(couponUsers);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#coupon.shopId")
    public void insertCouponAndCouponProds(@Valid Coupon coupon) {
        Integer suitableProdType = coupon.getSuitableProdType();
        if ((Objects.equals(CouponProdType.PROD_IN.value(), coupon.getSuitableProdType())
                || Objects.equals(CouponProdType.PROD_NO_IN.value(), coupon.getSuitableProdType()))
                && CollectionUtils.isEmpty(coupon.getCouponProds())) {
            throw new MaoshiShopBindException("商品不能为空");
        }
        if (coupon.getLimitNum() <= 0) {
            throw new MaoshiShopBindException("优惠券限领数量需大于0");
        }
        if (coupon.getValidTimeType() == 1 && coupon.getStartTime().getTime() > coupon.getEndTime().getTime()) {
            throw new MaoshiShopBindException("开始时间需大于结束时间");
        }
        // 结束时间小于等于当前时间
        Date nowTime = new Date();
        if (coupon.getValidTimeType() == 1 && coupon.getEndTime().getTime() < nowTime.getTime()) {
            coupon.setOverdueStatus(0);
            coupon.setPutonStatus(0);
        }
        couponMapper.insert(coupon);
        if (suitableProdType != 0) {
            List<Long> prodIds = coupon.getCouponProds().stream().map(CouponProd::getProdId).collect(Collectors.toList());
            // 插入所有商品
            couponProdMapper.insertCouponProdsBatch(coupon.getCouponId(), prodIds);
        }
    }

    @Override
    public Coupon getCouponAndCouponProdsByCouponId(Long id) {
        return couponMapper.getCouponAndCouponProdsByCouponId(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "couponAndCouponProds", key = "#coupon.shopId")
    public void receive(Coupon coupon, String userId) {
        //如果是积分商品，就扣除用户相应的积分
        if (Objects.equals(coupon.getCouponType(), 3)){
            applicationContext.publishEvent(new ScoreOrderEvent(coupon.getCouponId(),null,coupon.getScorePrice()));
        }
        CouponUser couponUser = getCouponUser(coupon, userId);
        couponUserMapper.insert(couponUser);
        // 更新库存
        if (couponMapper.updateCouponStocksAndVersion(coupon.getCouponId()) < 1) {
            throw new MaoshiShopBindException("优惠券库存不足");
        }
    }

    private CouponUser getCouponUser(Coupon coupon, String userId){
        Date nowTime = new Date();
        // 当优惠券状态不为投放时
        if (coupon.getOverdueStatus() == 0 || coupon.getPutonStatus() != 1) {
            throw new MaoshiShopBindException("该券无法被领取");
        }
        // 当优惠券不在规定时间内(类型为固定时间) 优惠券使用时间，不是领取时间，一般优惠券都是可以领，还不能用的
//        if (coupon.getValidTimeType() == 1 && (coupon.getEndTime().getTime() < nowTime.getTime() || coupon.getStartTime().getTime() > nowTime.getTime())) {
//            throw new MaoshiShopBindException("不在可领取的时间范围内");
//        }
        // 当优惠券无库存时
        if (coupon.getStocks() == 0) {
            throw new MaoshiShopBindException("该券领完了！");
        }
        int count = couponUserMapper.selectCount(new LambdaQueryWrapper<CouponUser>().eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, coupon.getCouponId()));
        if (count >= coupon.getLimitNum()) {
            throw new MaoshiShopBindException("该券已达个人领取上限，无法继续领取");
        }
        CouponUser couponUser = new CouponUser();
        couponUser.setUserId(userId);
        couponUser.setCouponId(coupon.getCouponId());
        couponUser.setStatus(1);
        couponUser.setReceiveTime(nowTime);
        // 生效时间类型为固定时间
        if (coupon.getValidTimeType() == 1) {
            couponUser.setUserStartTime(coupon.getStartTime());
            couponUser.setUserEndTime(coupon.getEndTime());
        }
        // 生效时间类型为领取后生效
        if (coupon.getValidTimeType() == 2) {
            couponUser.setUserStartTime(DateUtils.addDays(DateUtil.beginOfDay(nowTime), coupon.getAfterReceiveDays()));
            couponUser.setUserEndTime(DateUtils.addDays(couponUser.getUserStartTime(), coupon.getValidDays() + 1));
        }
        couponUser.setIsDelete(0);
        return couponUser;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeCoupon(Date now) {
        // 设置店铺的过期优惠券为过期状态
        couponMapper.updateOverdueStatusByTime(now);
        // 设置店铺库存为0优惠券的状态(设为未投放状态)
        couponMapper.updatePutonStatusByStocks();
    }

    @Override
    public void putonCoupon(Date now) {
        couponMapper.putonCoupon(now);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByCouponId(Long couponId) {
        // 获取未被删除的优惠券数量
        Integer useCount = couponUserMapper.selectCount(new LambdaQueryWrapper<CouponUser>().eq(CouponUser::getCouponId, couponId).eq(CouponUser::getIsDelete, 0));
        if (useCount > 0) {
            throw new MaoshiShopBindException("该优惠券已被领取，删除失败");
        }

        Coupon coupon = couponMapper.selectById(couponId);
        if (Objects.equals(coupon.getPutonStatus(), 1)) {
            throw new MaoshiShopBindException("该优惠券已投放，删除失败");
        }
        //删除优惠券关联的商品
        couponProdMapper.delete(new LambdaQueryWrapper<CouponProd>().eq(CouponProd::getCouponId, couponId));
        //删除优惠券
        couponMapper.deleteById(couponId);
    }

    @Override
    public IPage<CouponDto> pageProdCoupon(IPage<CouponDto> page, String userId) {
        return couponMapper.pageProdCoupon(page, userId);
    }

    @Override
    public IPage<CouponDto> generalCouponList(IPage<CouponDto> page, String userId) {
        return couponMapper.generalCouponList(page, userId);
    }

    @Override
    public IPage<CouponDto> getCouponListByStatus(IPage<CouponDto> page, String userId, Integer status) {
        IPage<CouponDto> couponDtoIPage = couponMapper.getCouponListByStatus(page, userId, status);
        List<CouponDto> couponDtoList = couponDtoIPage.getRecords();
        for (CouponDto couponDto : couponDtoList) {
            if (Objects.equals(couponDto.getValidTimeType(), 2)) {
                Integer afterReceiveDays = couponDto.getAfterReceiveDays();
                couponDto.setStartTime(DateUtil.offsetDay(couponDto.getReceiveTime(), afterReceiveDays));
                couponDto.setEndTime(DateUtil.offsetDay(couponDto.getStartTime(), couponDto.getValidDays()));
            }
        }
        return couponDtoIPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "CouponAndCouponUser", key = "#userId")
    public void deleteUserCouponByCouponId(String userId, Long couponId) {
        couponUserMapper.deleteUserCoupon(userId, couponId);
    }

    @Override
    public List<CouponOrderDto> getCouponListByShopIds(String userId, Long shopId) {
        return couponMapper.getCouponListByShopIds(userId, shopId);
    }

    @Override
    @Cacheable(cacheNames = "couponAndCouponProds", key = "#shopId")
    public List<Coupon> listPutonByShopId(Long shopId) {
        return couponMapper.listPutonCouponAndCouponProdsByShopId(shopId);
    }

    @Override
    public List<Long> listCouponIdsByUserId(String userId) {
        return couponMapper.listCouponIdsByUserId(userId);
    }

    @Override
    public IPage<ProductDto> prodListByCoupon(PageParam<ProductDto> page, Coupon coupon) {
        // 优惠券所有商品可用、指定商品可用、指定商品不可用 (0全部商品参与 1指定商品参与 2指定商品不参与)
        IPage<ProductDto> productDtoIPage;
        if (coupon.getSuitableProdType() == 0) {
            productDtoIPage = productMapper.listByShopId(page, coupon.getShopId());
        } else {
            productDtoIPage = productMapper.listBySuitableProdTypeAndCouponIdAndShopId(page, coupon.getShopId(), coupon.getSuitableProdType(), coupon.getCouponId());
        }
        return productDtoIPage;
    }

    @Override
    public Map<String, Long> getCouponCountByStatus(String userId) {
        return couponMapper.getCouponCountByStatus(userId);
    }

    @Override
    public IPage<Coupon> getPlatformPage(PageParam<Coupon> page, Coupon coupon) {
        return couponMapper.getPlatformPage(page, coupon);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Cacheable(cacheNames = "couponAndCouponProds", key = "#coupon.shopId")
    public void offline(Coupon coupon, String offlineReason, Long sysUserId) {
        // 添加下线处理记录
        Date now = new Date();
        OfflineHandleEvent offlineHandleEvent = new OfflineHandleEvent();
        offlineHandleEvent.setHandleId(coupon.getCouponId());
        offlineHandleEvent.setHandleType(OfflineHandleEventType.COUPON.getValue());
        offlineHandleEvent.setCreateTime(now);
        offlineHandleEvent.setOfflineReason(offlineReason);
        offlineHandleEvent.setHandlerId(sysUserId);
        offlineHandleEvent.setShopId(coupon.getShopId());
        offlineHandleEvent.setStatus(OfflineHandleEventStatus.OFFLINE_BY_PLATFORM.getValue());
        offlineHandleEvent.setUpdateTime(now);
        offlineHandleEventService.save(offlineHandleEvent);

        // 更新活动状态为下线
        couponMapper.updatePutOnStatusByCouponId(coupon.getCouponId(), CouponStatusEnum.OFFLINE.getValue());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditCoupon(OfflineHandleEventAuditParam offlineHandleEventAuditParam, Long sysUserId) {
        // 审核通过
        if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.AGREE_BY_PLATFORM.getValue())) {
            // 更新优惠券为等待投放状态
            couponMapper.updatePutOnStatusByCouponId(offlineHandleEventAuditParam.getHandleId(), CouponStatusEnum.WAIT_PUT_ON.getValue());
        }
        // 审核不通过
        else if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.DISAGREE_BY_PLATFORM.getValue())) {
            couponMapper.updatePutOnStatusByCouponId(offlineHandleEventAuditParam.getHandleId(), CouponStatusEnum.OFFLINE.getValue());
        }
        offlineHandleEventService.auditOfflineEvent(offlineHandleEventAuditParam, sysUserId);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditApply(Long eventId, Long couponId, String reapplyReason) {
        // 更新活动为待审核状态
        couponMapper.updatePutOnStatusByCouponId(couponId, CouponStatusEnum.WAIT_AUDIT.getValue());

        // 更新事件状态
        offlineHandleEventService.updateToApply(eventId, reapplyReason);
    }

}
